package com.fowo.api.sys.service.impl;

import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fowo.api.common.model.RException;
import com.fowo.api.common.model.SimpleItem;
import com.fowo.api.sys.annotation.DataActivity;
import com.fowo.api.sys.constants.SysEntityConstants;
import com.fowo.api.sys.entity.*;
import com.fowo.api.sys.entity.enums.SysDataActivityAction;
import com.fowo.api.sys.mapper.*;
import com.fowo.api.sys.model.*;
import com.fowo.api.sys.service.SysRoleService;
import org.springframework.beans.BeanUtils;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Service
public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> implements SysRoleService {

    @Resource
    private SysRoleMenuMapper roleMenuMapper;

    @Resource
    private SysRolePermissionMapper rolePermissionMapper;

    @Resource
    private SysRolePermissionDepartmentsMapper rolePermissionDepartmentsMapper;

    @Resource
    private SysRoleDataMapper sysRoleDataMapper;

    @Resource
    private SysRoleDataUserMapper sysRoleDataUserMapper;

    @Resource
    private SysUserRoleMapper sysUserRoleMapper;

    @Resource
    private DataSource dataSource;

    private final IdentifierGenerator identifierGenerator = new DefaultIdentifierGenerator();

    @Override
    public List<SimpleItem> getSimplesByUserId(Long userId) {
        return getBaseMapper().selectSimplesByUserId(userId);
    }

    @DataActivity(action = SysDataActivityAction.Add)
    @Transactional(rollbackFor = Exception.class)
    @Override
    public SysRole create(CreateSysRole model) {
        if (Boolean.TRUE.equals(baseMapper.selectExistsRoleName(model.getRoleName(), null))) {
            throw new RException("角色名称不能重复");
        }
        SysRole sysRole = new SysRole();
        BeanUtils.copyProperties(model, sysRole);
        sysRole.doCreate();
        if(save(sysRole)) {
            // 创建默认相关的数据权限
            SysRolePermission srp = new SysRolePermission()
                .setRoleId(sysRole.getId())
                .setRange(SysEntityConstants.ROLE_PERMISSION_RANGE_ALL);
            rolePermissionMapper.insert(srp);

            if (model.getMenus() != null) {
                updateRoleMenusByRoleId(sysRole.getId(), model.getMenus());
            }
            return sysRole;
        }
        return null;
    }

    @DataActivity(action = SysDataActivityAction.Add)
    @Transactional(rollbackFor = Exception.class)
    @Override
    public SysRole copy(CopySysRole model) {
        SysRoleVo copyForm = getVoById(model.getCopyFormId());
        if (copyForm == null) {
            throw new RException("未找到复制源角色！");
        }
        CreateSysRole createSysRole = new CreateSysRole();
        BeanUtils.copyProperties(model, createSysRole);
        createSysRole.setMenus(copyForm.getMenus());
        SysRole sysRole = create(createSysRole);
        if (sysRole != null) {
            SysRolePermissionVo permissionVo = getRolePermission(copyForm.getId());
            if (permissionVo != null) {
                permissionVo.setRoleId(sysRole.getId());
                updateRolePermission(permissionVo);
            }
        }
        return sysRole;
    }

    @Override
    public Page<SysRoleItemVo> searchPage(SysRoleSearchParamPo search) {
        search.initSort("sr.sort desc", "sr.");
        return getBaseMapper().searchPage(search.toPage(), search);
    }

    @Override
    public List<SysRoleItemVo> search(SysRoleSearchParamPo search) {
        return getBaseMapper().search(search);
    }

    @DataActivity(action = SysDataActivityAction.Modified)
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean update(SysRoleVo sysRoleVo) {
        if (sysRoleVo.getRoleName() != null && Boolean.TRUE.equals(baseMapper.selectExistsRoleName(sysRoleVo.getRoleName(), sysRoleVo.getId()))) {
            throw new RException("角色名称不能重复");
        }
        SysRole updateRole = new SysRole();
        BeanUtils.copyProperties(sysRoleVo, updateRole);
        if(getBaseMapper().updateById(updateRole) > 0) {
            if(sysRoleVo.getMenus() != null) {
                updateRoleMenusByRoleId(sysRoleVo.getId(), sysRoleVo.getMenus());
            }
            return true;
        }
        return false;
    }
    
    @DataActivity(action = SysDataActivityAction.View)
    @Override
    public SysRoleVo getVoById(Long id) {
        SysRole sysRole = getById(id);
        if(sysRole != null) {
            SysRoleVo sysRoleVo = new SysRoleVo();
            BeanUtils.copyProperties(sysRole, sysRoleVo);
            sysRoleVo.setMenus(roleMenuMapper.selectMenuIdsByUserId(id));
            return sysRoleVo;
        }
        return null;
    }

    private void updateRoleMenusByRoleId(Long id, List<Long> menus) {
        roleMenuMapper.delete(Wrappers.<SysRoleMenu>query().eq("role_id", id));
        List<SysRoleMenu> insertList = new ArrayList<>();
        for(Long menuId : menus) {
            SysRoleMenu sysRoleMenu = new SysRoleMenu();
            sysRoleMenu.setId(identifierGenerator.nextId(null).longValue());
            sysRoleMenu.setRoleId(id);
            sysRoleMenu.setMenuId(menuId);
            sysRoleMenu.doCreate();
            insertList.add(sysRoleMenu);
        }
        if (insertList.isEmpty()) {
            return;
        }
        // 使用快速批量插入
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
        int[] updates = jdbcTemplate.batchUpdate("insert into sys_role_menu (id, role_id, menu_id, create_time, last_update_time, del_flag) values (?, ?, ?, ?, ?, 0)", new BatchPreparedStatementSetter() {
            @Override
            public void setValues(PreparedStatement ps, int i) throws SQLException {
                SysRoleMenu sysRoleMenu = insertList.get(i);
                ps.setLong(1, sysRoleMenu.getId());
                ps.setLong(2, sysRoleMenu.getRoleId());
                ps.setLong(3, sysRoleMenu.getMenuId());
                ps.setTimestamp(4, Timestamp.from(sysRoleMenu.getCreateTime().toInstant()));
                ps.setTimestamp(5, Timestamp.from(sysRoleMenu.getLastUpdateTime().toInstant()));
            }

            @Override
            public int getBatchSize() {
                return insertList.size();
            }
        });
        log.debug(String.format("BATCH INSERT sys_role_menu %d records", Arrays.stream(updates).sum()));
    }

    @DataActivity(action = SysDataActivityAction.Del)
    @Override
    public boolean removeById(Serializable id) {
        SysRole sysRole = getById(id);
        if(sysRole != null && sysRole.isProtected()) {
            // 被保护的数据不能删除
            throw new RException("被保护的数据不能删除");
        }
        return super.removeById(id);
    }

    @Override
    public SysRolePermissionVo getRolePermission(Long id) {
        SysRole sysRole = getById(id);
        if (sysRole == null) {
            return null;
        }
        SysRolePermission rolePermission = baseMapper.selectRolePermissionByRoleId(id);
        if (rolePermission == null) {
            return null;
        }
        SysRolePermissionVo vo = new SysRolePermissionVo();
        BeanUtils.copyProperties(rolePermission, vo);
        vo.setRoleName(sysRole.getRoleName());
        if (rolePermission.getRange() == SysEntityConstants.ROLE_PERMISSION_RANGE_CUSTOM) {
            vo.setDepartmentIds(new ArrayList<>());
            baseMapper.selectRolePermissionDepartmentsByRoleId(rolePermission.getId())
                .forEach(rpd -> vo.getDepartmentIds().add(rpd.getDepartmentId()));
        }
        return vo;
    }

    @Override
    @Transactional
    public void updateRolePermission(SysRolePermissionVo vo) {
        SysRolePermission rolePermission = baseMapper.selectRolePermissionByRoleId(vo.getRoleId());
        boolean create = false;
        if (rolePermission == null) {
            create = true;
            rolePermission = new SysRolePermission().setRoleId(vo.getRoleId());
        }
        rolePermission.setRange(vo.getRange());
        int isOk = create ? rolePermissionMapper.insert(rolePermission) : rolePermissionMapper.updateById(rolePermission);
        if (isOk == 0) {
            throw new RException("未能正常访问数据库，请稍后再试");
        }
        if (vo.getDepartmentIds() != null) {
            rolePermissionDepartmentsMapper.delete(
                Wrappers.<SysRolePermissionDepartments>query()
                .eq("role_permission_id", rolePermission.getId())
            );
            if (rolePermission.getRange() == SysEntityConstants.ROLE_PERMISSION_RANGE_CUSTOM) {
                for(Long departmentId : vo.getDepartmentIds()) {
                    rolePermissionDepartmentsMapper.insert(
                        new SysRolePermissionDepartments()
                            .setRolePermissionId(rolePermission.getId())
                            .setDepartmentId(departmentId)
                    );
                }
            }
        }
    }

    @Override
    public SysRoleDataVo getDataVoById(Long roleId) {
        SysRoleDataVo sysRoleDataVo = new SysRoleDataVo();
        sysRoleDataVo.setData(sysRoleDataMapper.selectList(Wrappers.<SysRoleData>query().eq("role_id", roleId)));
        sysRoleDataVo.setUsers(sysRoleDataUserMapper.selectList(Wrappers.<SysRoleDataUser>query().eq("role_id", roleId)).stream().map(SysRoleDataUser::getUserId).collect(Collectors.toList()));
        return sysRoleDataVo;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean updateData(Long roleId, SysRoleDataVo dataVo) {
        if (dataVo.getData() != null) {
            for (SysRoleData roleData : dataVo.getData()) {
                if (roleData.getId() == null) {
                    SysRoleData sysRoleData = new SysRoleData();
                    BeanUtils.copyProperties(roleData, sysRoleData);
                    sysRoleData.setRoleId(roleId);
                    sysRoleData.doCreate();
                    if(sysRoleDataMapper.insert(sysRoleData) != 1) {
                        throw new RException("插入权限数据失败");
                    }
                } else {
                    SysRoleData sysRoleData = sysRoleDataMapper.selectById(roleData.getId());
                    if (sysRoleData == null) {
                        throw new RException("更新权限数据异步，此权限数据已删除！");
                    }
                    BeanUtils.copyProperties(roleData, sysRoleData);
                    sysRoleData.setRoleId(roleId);
                    sysRoleData.doUpdate();
                    sysRoleDataMapper.updateById(sysRoleData);
                }
            }
        }
        if (dataVo.getUsers() != null) {
            sysRoleDataUserMapper.delete(Wrappers.<SysRoleDataUser>query().eq("role_id", roleId));
            for(Long userId : dataVo.getUsers()) {
                SysRoleDataUser sysRoleDataUser = new SysRoleDataUser();
                sysRoleDataUser.setRoleId(roleId);
                sysRoleDataUser.setUserId(userId);
                sysRoleDataUser.doCreate();
                if(sysRoleDataUserMapper.insert(sysRoleDataUser) != 1) {
                    throw new RException("更新角色权限人失败");
                }
            }
        }
        return true;
    }

    @Override
    public void removeUsersInRole(List<Long> userIds, Long roleId) {
        sysUserRoleMapper.delete(Wrappers.<SysUserRole>query()
                .eq("role_id", roleId)
                .in("user_id", userIds)
        );
    }

    @Override
    public void addUsersInRole(List<Long> userIds, Long roleId) {
        List<SysUserRole> sysUserRoles = sysUserRoleMapper.selectList(Wrappers.<SysUserRole>query()
                .eq("role_id", roleId)
                .in("user_id", userIds)
                .select("user_id")
        );
        for (Long userId : userIds) {
            if (sysUserRoles.stream().noneMatch(ur -> Objects.equals(ur.getUserId(), userId))) {
                SysUserRole newUserRole = new SysUserRole();
                newUserRole.setRoleId(roleId);
                newUserRole.setUserId(userId);
                newUserRole.doCreate();
                sysUserRoleMapper.insert(newUserRole);
            }
        }
    }

    @Override
    public void fillUserListRoles(List<SysUserItemVo> users) {
        if (users.isEmpty()) {
            return;
        }
        List<UserRoleNameItem> items = sysUserRoleMapper.selectUserRoleNameByUsers(users.stream().map(SysUser::getId).collect(Collectors.toList()));
        for (SysUserItemVo user : users) {
            user.setRoles(items.stream().filter(r -> Objects.equals(r.getUserId(), user.getId())).collect(Collectors.toList()));
        }
    }
}
